//------------------------------------------------------------------------------
// <copyright file="JsonString.h" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

#pragma once

#include <memory>

namespace Microsoft { namespace Internal { namespace GamesTest { namespace Rpc { namespace Server
{
	/// <summary>
	/// String returned from a JSON library.
	/// </summary>
	/// <remarks>
	/// Captures by reference where possible to avoid copying, and avoids getting string length.
	/// </remarks>
	class JsonString
	{
	private:
		#ifdef USE_PLATFORMSTRING
			Platform::String^ m_imp;
		#else
			const wchar_t* m_imp;
			std::unique_ptr<wchar_t[]> m_data;
			mutable unsigned long m_length;
		#endif

	private:
		#ifndef USE_PLATFORMSTRING
			static wchar_t* Duplicate(const wchar_t* string, unsigned long length)
			{
				wchar_t* duplicate = new wchar_t[length + 1];

				wcsncpy_s(duplicate, length + 1, string, length + 1);

				return duplicate;
			}
		#endif

	public:
		/// <summary>
		/// Default constructor.
		/// </summary>
		#ifdef USE_PLATFORMSTRING
			JsonString(): m_imp(nullptr) {}
		#else
			JsonString(): m_imp(L""), m_length(0) {}
		#endif

		/// <summary>
		/// Constructor with reference.
		/// </summary>
		/// <param name="reference">String to reference.</param>
		/// <param name="referenceLength">Length of string to reference.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString(Platform::String^ reference): m_imp(reference) {}

			JsonString(const wchar_t* reference, unsigned long referenceLength)
			{
				m_imp = Platform::StringReference(reference, referenceLength);
			}

			JsonString(const wchar_t* reference)
			{
				m_imp = Platform::StringReference(reference);
			}

			JsonString(const std::wstring& reference)
			{
				m_imp = Platform::StringReference(reference.c_str(), reference.length());
			}
		#else
			JsonString(const wchar_t* reference): m_imp(reference), m_length((unsigned long)wcslen(reference)) {}
			JsonString(const wchar_t* reference, unsigned long referenceLength): m_imp(reference), m_length(referenceLength) {}
			JsonString(const std::wstring& reference): m_imp(reference.c_str()), m_length((unsigned long)reference.length()) {}
		#endif

		/// <summary>
		/// Constructor with reference to another JsonString.
		/// </summary>
		/// <param name="reference">String to reference.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString(const JsonString& reference): m_imp(reference.m_imp) {}
		#else
			JsonString(const JsonString& reference): m_imp(reference.m_imp), m_length(reference.m_length)
			{
				// Copy if the other string is also a copy because it could be destroyed any time.
				if (reference.m_data.get() != nullptr)
				{
					m_data.reset(Duplicate(reference.m_imp, reference.m_length));
					m_imp = m_data.get();
				}
			}
		#endif

		/// <summary>
		/// Constructor with copy.
		/// </summary>
		/// <param name="copy">String to copy.</param>
		/// <param name="copyLength">Length of string to copy.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString(wchar_t* copy): m_imp(ref new Platform::String(copy)) {}
		#else
			JsonString(wchar_t* copy, unsigned long copyLength): m_length(copyLength)
			{
				m_data.reset(Duplicate(copy, copyLength));
				m_imp = m_data.get();
			}
		#endif

	public:
		/// <summary>
		/// Gets the string length.
		/// </summary>
		unsigned long GetLength() const
		{
			#ifdef USE_PLATFORMSTRING
				return m_imp == nullptr ? 0 : (unsigned long)m_imp->Length();
			#else				
				return m_length;
			#endif
		}

		/// <summary>
		/// Checks whether the string is empty.
		/// </summary>
		bool Empty() const
		{
#ifdef USE_PLATFORMSTRING
			return m_imp == nullptr;
#else
			if (m_imp == nullptr)
			{
				return true;
			}

			return m_length == 0;
#endif
		}

		/// <summary>
		/// Gets the string data.
		/// </summary>
		operator const wchar_t*() const
		{
			#ifdef USE_PLATFORMSTRING
				if (m_imp == nullptr)
				{
					return L"";
				}
				else
				{
					return m_imp->Data();
				}
			#else
				return m_imp;
			#endif
		}

		/// <summary>
		/// Gets the string data.
		/// </summary>
		#ifdef USE_PLATFORMSTRING
		operator Platform::String^() const
		{
			return m_imp;
		}
		#endif

		/// <summary>
		/// Returns empty string for convenience.
		/// </summary>
		static JsonString EmptyString()
		{
			return JsonString();
		}

		/// <summary>
		/// Assignment with reference.
		/// </summary>
		/// <param name="reference">String to reference.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString& operator=(Platform::String^ reference)
			{
				m_imp = reference;
			}
		#else
			JsonString& operator=(const wchar_t* reference)
			{
				m_imp = reference;
				m_length = (unsigned long)wcslen(reference);
				m_data.release();
			}
		#endif

		/// <summary>
		/// Assignment with reference to another JsonString.
		/// </summary>
		/// <param name="reference">String to reference.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString& operator=(const JsonString& reference)
			{
				m_imp = reference.m_imp;
				return *this;
			}
		#else
			JsonString& operator=(const JsonString& reference)
			{
				m_length = reference.m_length;

				if (reference.m_data.get() != nullptr)
				{
					m_data.reset(Duplicate(reference.m_imp, reference.m_length));
					m_imp = m_data.get();					
				}
				else
				{
					m_imp = reference.m_imp;
					m_data.release();
				}

				return *this;
			}
		#endif

		/// <summary>
		/// Assignment with copy.
		/// </summary>
		/// <param name="copy">String to copy.</param>
		#ifdef USE_PLATFORMSTRING
			JsonString& operator=(wchar_t* copy)
			{
				m_imp = ref new Platform::String(copy);
			}
		#else
			JsonString& operator=(wchar_t* copy)
			{
				m_length = (unsigned long)wcslen(copy);
				m_data.reset(Duplicate(copy, m_length));
				m_imp = m_data.get();			
			}
		#endif
	};

}}}}}